package com.woniu.filter;//package com.woniu.filter;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.text.AntPathMatcher;
import cn.hutool.json.JSONUtil;
import com.woniu.config.AuthenticationUrlProperties;
import com.woniu.execption.TokenException;
import com.woniu.utils.JwtUtil;
import com.woniu.utils.RedisUtil;
import com.woniu.utils.Result;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//@Order(2)
//@Component
public class AuthenticationFilter implements GlobalFilter {
    @Autowired
    private AuthenticationUrlProperties urls;
    @Autowired
    private RedisUtil redisUtil;

    private AntPathMatcher pm = new AntPathMatcher();

    private boolean isBaseUrl(String url){
        if (urls.getBaseUrls()!=null&&urls.getBaseUrls().size()>0){
            for (String baseUrl : urls.getBaseUrls()) {
                if (pm.match(baseUrl,url)){
                    return true;
                }
            }
        }
        return false;
    }


    private boolean isAdminUrl(String url){
        System.out.println("admin");
        System.out.println(urls.getSystemUrls());
        System.out.println(url);
        if (urls.getSystemUrls()!=null&&urls.getSystemUrls().size()>0){
            for (String systemUrl : urls.getSystemUrls()) {
                if (pm.match(systemUrl,url)){
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isStationBossUrl(String url){
       // System.out.println("stationBoss");
        System.out.println(urls.getSystemUrls());
        System.out.println(url);
        if (urls.getSystemUrls()!=null&&urls.getSystemUrls().size()>0){
            for (String systemUrl : urls.getSystemUrls()) {
                if (pm.match(systemUrl,url)){
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isUserUrl(String url){
        if (urls.getUserUrls()!=null&&urls.getUserUrls().size()>0){
            for (String userUrl : urls.getUserUrls()) {
                if (pm.match(userUrl,url)){
                    return true;
                }
            }
        }
        return false;
    }
    private Mono<Void> response(ServerWebExchange exchange){
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
        Result<String> result = Result.newInstance(String.class).setMessage("未认证").setCode(401);
        String json = JSONUtil.parse(result).toString();
        return exchange.getResponse().writeWith(Mono.fromSupplier(()->{
            return exchange.getResponse().bufferFactory().wrap(json.getBytes());
        }));
    }

//    private Mono<Void> tokenExpire(ServerWebExchange exchange,String message,Integer code){
//        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
//        Result<String> result = Result.newInstance(String.class).setMessage(message).setCode(code);
//        String json = JSONUtil.parse(result).toString();
//        return exchange.getResponse().writeWith(Mono.fromSupplier(()->{
//            return exchange.getResponse().bufferFactory().wrap(json.getBytes());
//        }));
//    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("======登录认证=======");
        String url = exchange.getRequest().getPath().toString();


        System.out.println(url);
        //判断该资源是否需要登录(公共资源)
        if (isBaseUrl(url)) return chain.filter(exchange);
        else{
            List<String> headers = exchange.getRequest().getHeaders().get("token");
            String token = null;
            if (headers !=null && headers.size()>0) token = headers.get(0);
            System.out.println(token);
            //判断当前要访问的资源是管理资源
            if (url.endsWith(".api")){
                System.out.println(isAdminUrl(url));
                if (isAdminUrl(url)) return chain.filter(exchange);
                else {
                    //解析管理员token
                    if (token==null){
                        throw new TokenException("未认证",401);
                        //throw new Exception("未登录");

                    }else {
                        try {
                            JwtUtil.parseAdminToken(token);
                            chain.filter(exchange);
                        } catch (Exception e) {
                            e.printStackTrace();
                            throw new TokenException("未认证",401);
                            //return response(exchange);
                            //throw new RuntimeException(e);
                        }
                    }
                }
            }else {
                if (isUserUrl(url)) return chain.filter(exchange);
                else {
                    System.out.println("这里");
                    //解析用户token 未登录的情况
                    if (token==null||token.equals("")){
                        System.out.println("token为空");
                        return response(exchange);
                    }else {
                        //解析token
                        try {
                            //解析用户的短token
                            Claims claims = JwtUtil.parseUserToken(token);
                            if ((claims.getExpiration().getTime()-new Date().getTime())/1000<30){
                                throw new ExpiredJwtException(null,claims,"accessToken剩余时间不足");
                            }
                        }catch (Exception e){
                            try {
                                //解析异常 直接响应给前端
                                userTokenAddTime(token,exchange);
                            } catch (Exception ex) {
                                return response(exchange);
                            }
                        }
                        return chain.filter(exchange);
                    }
                }
            }
            //判断当前要访问的资源是用户资源
        }


        return chain.filter(exchange);
    }

    private void userTokenAddTime(String token,ServerWebExchange exchange) throws Exception {
        HttpHeaders response = exchange.getResponse().getHeaders();
        try {
            //解析用户的短token
            Claims claims = JwtUtil.parseUserToken(token);
            if ((claims.getExpiration().getTime()-new Date().getTime())/1000<30){
                throw new ExpiredJwtException(null,claims,"accessToken剩余时间不足");
            }
        }catch (Exception e){

            //accessToken 已过期 过期有两种情况 一种是短token过期 一种是长token过期
            //取出refreshToken
            String refreshToken = redisUtil.get(token);
            if (refreshToken==null){
                System.out.println("长token过期了");
                throw e;
            }
            Claims claims = JwtUtil.parseUserToken(refreshToken);
            if ((claims.getExpiration().getTime()-new Date().getTime())/1000/60<10) {
                System.out.println("长token不足10分钟");
                //refreshToken 不足十分钟
                throw e;
            }
            //15分钟未操作 重新生成长短 token
            Map<String, Object> map = new HashMap<>();
            map.put("id",claims.get("id"));
            map.put("phone",claims.get("phone"));
            map.put("username",claims.get("username"));
            map.put("alipayUsername",claims.get("alipayUsername"));
            map.put("alipayAccount",claims.get("alipayAccount"));
            map.put("accountId",claims.get("accountId"));
            map.put("realName",claims.get("realName"));
            map.put("identityCard",claims.get("identityCard"));
            map.put("nickName",claims.get("nickName"));
            map.put("enrollTime",claims.get("enrollTime"));
            map.put("avatarPath",claims.get("avatarPath"));
            map.put("credibility",claims.get("credibility"));
            map.put("realNameState",claims.get("realNameState"));
            map.put("score",claims.get("score"));
            map.put("state",claims.get("state"));
            //HashMap<String,Object> accessBody= BeanUtil.beanToMap(accountDto);

            HashMap<String, Object> accessBody = new HashMap<>();
            //使用雪花算法 生成随机值,确保用户的短token 不重复 因为用户有可能 在同一毫秒注册 而body是由用户注册的时间来决定的
            String random = new Snowflake(1, 1).nextIdStr();
            accessBody.put("random",random);
            //生成双token  accessToken 用于返回前端 不带敏感信息
            String newAccessToken = JwtUtil.createUserToken("token",accessBody);
            //refreshToken 存入redis 带敏感信息
            String newRefreshToken = JwtUtil.createToken(map, 24*3600,JwtUtil.userSecret);
            //短token 返回给前端
            response.set("token",newAccessToken);
            response.set("Access-Control-Expose-Headers","token");
            //长token保存到redis
            redisUtil.set(newAccessToken,newRefreshToken);
        }
    }

}
